package graphing;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;

public class GraphData {
	public static final byte SCATTER = 0;
	public static final byte SEQUENTIAL = 1;
	
	public static final byte CONNECTED = 0;
	public static final byte LARGE_DOTS = 1;
	public static final byte SMALL_DOTS = 2;
	
	//TODO separate window data and actual point data
	//create system (possibly) for hovering over points to get their value
	//create system for animating graphs (w/ keystrokes)
	private Point2D.Float domain;
	private Point2D.Float range;
	public float[] yData;
	public float[] xData;
	public byte type;
	public byte displayMethod;
	public Color color;
	public boolean autoscale;
	public float delta;
	public float start;
	
	public GraphData(){
		domain = new Point2D.Float(-10, 10);
		range = new Point2D.Float(-10, 10);
		type = 1;
		displayMethod = 0;
		xData = new float[0];
		yData = new float[0];
		color = Color.red;
		autoscale = true;
		delta = 1;
		start = 0;
	}
	
	public GraphData(float[] x, float[] y){
		domain = new Point2D.Float(-10, 10);
		range = new Point2D.Float(-10, 10);
		type = 1;
		displayMethod = 0;
		xData = x;
		yData = y;
		color = Color.red;
		delta = 1;
		start = 0;
	}
	
	public Point2D.Float getDomain(){
		if(autoscale){
			if(type == GraphData.SEQUENTIAL){
				domain = new Point2D.Float(start, start+delta*(yData.length-1));
			}else{
				float maxd = Float.NEGATIVE_INFINITY;
				float mind = Float.POSITIVE_INFINITY;
				for(int n = 0; n < xData.length; n++){
					if(xData[n] < mind) mind = xData[n];
					if(xData[n] > maxd) maxd = xData[n];
				}
				domain = new Point2D.Float(mind, maxd);
			}
		}
		return domain;
	}
	
	public void setDomain(float min, float max){
		domain = new Point2D.Float(min,max);
	}
	
	public void setRange(float min, float max){
		range = new Point2D.Float(min,max);
	}
	
	public Point2D.Float getRange(){
		if(autoscale){
			float maxr = Float.NEGATIVE_INFINITY;
			float minr = Float.POSITIVE_INFINITY;
			for(int n = 0; n < yData.length; n++){
				if(yData[n] < minr) minr = yData[n];
				if(yData[n] > maxr) maxr = yData[n];
			}
			range = new Point2D.Float(minr, maxr);
		}
		return range;
	}
	
	public void drawGraph(Graphics2D g, int width, int height){
		if(yData.length > 0){
			getRange();
			getDomain();
			float mx = ((width-10)/(domain.y-domain.x));
			float bx = domain.x-5/mx;
			float my = ((height-10)/(range.y-range.x));
			float by = range.x-5/my;
			Point2D.Float current;
			Point2D.Float next;
			g.setColor(color);
			if(type == GraphData.SEQUENTIAL){
				current = new Point2D.Float(start, yData[0]);
				for(int i = 1; i < yData.length; i++){
					next = new Point2D.Float(start+i*delta, yData[i]);
					if(displayMethod == GraphData.CONNECTED){
						g.drawLine((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), (int)((next.x-bx)*mx), (int)(height-(next.y-by)*my));
					}else if(displayMethod == GraphData.LARGE_DOTS){
						g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 8, 8);
					}else{
						g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 3, 3);
					}
					current = next;
				}
				if(displayMethod == GraphData.LARGE_DOTS){
					g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 8, 8);
				}else if(displayMethod == GraphData.SMALL_DOTS){
					g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 3, 3);
				}
			}else{
				int[] order = new int[xData.length];
				for(int i = 0; i<order.length; i++){
					order[i] = i;
				}
				order = parallelSort(xData, order);
				current = new Point2D.Float(xData[order[0]], yData[order[0]]);
				for(int i = 1; i < yData.length; i++){
					next = new Point2D.Float(xData[order[i]], yData[order[i]]);
					if(displayMethod == GraphData.CONNECTED){
						g.drawLine((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), (int)((next.x-bx)*mx), (int)(height-(next.y-by)*my));
					}else if(displayMethod == GraphData.LARGE_DOTS){
						g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 8, 8);
					}else{
						g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 3, 3);
					}
					current = next;
				}
				if(displayMethod == GraphData.LARGE_DOTS){
					g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 8, 8);
				}else if(displayMethod == GraphData.SMALL_DOTS){
					g.fillOval((int)((current.x-bx)*mx), (int)(height-(current.y-by)*my), 3, 3);
				}
			}
		}
	}
	
	private int[] parallelSort(float[] a, int[] b){
		float[] n = new float[a.length];
		for(int i = 0; i < n.length; i++){
			n[i] = a[i];
		}
		float temp;
		int temp2;
		for(int i = 0; i < n.length; i++){
			for(int k = 0; k < n.length-i-1; k++){
				if(n[k] > n[k+1]){
					temp = n[k+1];
					n[k+1] = n[k];
					n[k] = temp;
					temp2 = b[k+1];
					b[k+1] = b[k];
					b[k] = temp2;
				}
			}
		}
		return b;
	}
}
